package com.gitee.dolt.database.datasource;


import com.gitee.dolt.database.properties.DatabaseProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.Advisor;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.*;
import org.springframework.web.bind.annotation.RestController;

import java.lang.annotation.Annotation;
import java.util.*;

/**
 * @author LYH
 */
@Slf4j
@RequiredArgsConstructor
public abstract class BaseDatabaseConfiguration {

    protected final DatabaseProperties databaseProperties;

    private static final List<Class<? extends Annotation>> AOP_POINTCUT_ANNOTATIONS = new ArrayList<>(2);

    static {
        // 事务在controller层开启。
        AOP_POINTCUT_ANNOTATIONS.add(RestController.class);
        AOP_POINTCUT_ANNOTATIONS.add(Controller.class);
    }

    protected TransactionAttributeSource transactionAttributeSource() {
        /*当前存在事务就使用当前事务，当前不存在事务就创建一个新的事务*/
        RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute();
        requiredTx.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Throwable.class)));
        requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        requiredTx.setTimeout(this.databaseProperties.getTxTimeout());
        Map<String, TransactionAttribute> txMap = new HashMap<>(this.databaseProperties.getTransactionAttributeList().size() + 5);
        this.databaseProperties.getTransactionAttributeList().forEach((key) -> txMap.put(key, requiredTx));

        RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
        readOnlyTx.setReadOnly(true);
        readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);
        //除了上面的事物以外，都走只读事物
        txMap.put("*", readOnlyTx);
        NameMatchTransactionAttributeSource txTransactionAttributeSource = new NameMatchTransactionAttributeSource();
        txTransactionAttributeSource.setNameMap(txMap);
        return txTransactionAttributeSource;
    }

    protected Advisor txAdviceAdvisor(TransactionInterceptor ti) {
        return new DefaultPointcutAdvisor(new Pointcut() {
            @Override
            public MethodMatcher getMethodMatcher() {
                return MethodMatcher.TRUE;
            }

            @Override
            public ClassFilter getClassFilter() {
                return (clazz) -> {
                    List<String> transactionScanPackages = databaseProperties.getTransactionScanPackages();
                    for (String transactionScanPackage : transactionScanPackages) {
                        if (clazz.getName().startsWith(transactionScanPackage)) {
                            for (Class<? extends Annotation> aop : AOP_POINTCUT_ANNOTATIONS) {
                                if (clazz.getAnnotation(aop) == null) {
                                    continue;
                                }
                                return true;
                            }
                        }
                    }
                    return false;
                };
            }
        }, ti);
    }

}
